`define AXI_BURST_TYPE_FIXED                                2'b00
`define AXI_BURST_TYPE_INCR                                 2'b01
`define AXI_BURST_TYPE_WRAP                                 2'b10
// Access permissions
`define AXI_PROT_UNPRIVILEGED_ACCESS                        3'b000
`define AXI_PROT_PRIVILEGED_ACCESS                          3'b001
`define AXI_PROT_SECURE_ACCESS                              3'b000
`define AXI_PROT_NON_SECURE_ACCESS                          3'b010
`define AXI_PROT_DATA_ACCESS                                3'b000
`define AXI_PROT_INSTRUCTION_ACCESS                         3'b100
// Memory types (AR)
`define AXI_ARCACHE_DEVICE_NON_BUFFERABLE                   4'b0000
`define AXI_ARCACHE_DEVICE_BUFFERABLE                       4'b0001
`define AXI_ARCACHE_NORMAL_NON_CACHEABLE_NON_BUFFERABLE     4'b0010
`define AXI_ARCACHE_NORMAL_NON_CACHEABLE_BUFFERABLE         4'b0011
`define AXI_ARCACHE_WRITE_THROUGH_NO_ALLOCATE               4'b1010
`define AXI_ARCACHE_WRITE_THROUGH_READ_ALLOCATE             4'b1110
`define AXI_ARCACHE_WRITE_THROUGH_WRITE_ALLOCATE            4'b1010
`define AXI_ARCACHE_WRITE_THROUGH_READ_AND_WRITE_ALLOCATE   4'b1110
`define AXI_ARCACHE_WRITE_BACK_NO_ALLOCATE                  4'b1011
`define AXI_ARCACHE_WRITE_BACK_READ_ALLOCATE                4'b1111
`define AXI_ARCACHE_WRITE_BACK_WRITE_ALLOCATE               4'b1011
`define AXI_ARCACHE_WRITE_BACK_READ_AND_WRITE_ALLOCATE      4'b1111
// Memory types (AW)
`define AXI_AWCACHE_DEVICE_NON_BUFFERABLE                   4'b0000
`define AXI_AWCACHE_DEVICE_BUFFERABLE                       4'b0001
`define AXI_AWCACHE_NORMAL_NON_CACHEABLE_NON_BUFFERABLE     4'b0010
`define AXI_AWCACHE_NORMAL_NON_CACHEABLE_BUFFERABLE         4'b0011
`define AXI_AWCACHE_WRITE_THROUGH_NO_ALLOCATE               4'b0110
`define AXI_AWCACHE_WRITE_THROUGH_READ_ALLOCATE             4'b0110
`define AXI_AWCACHE_WRITE_THROUGH_WRITE_ALLOCATE            4'b1110
`define AXI_AWCACHE_WRITE_THROUGH_READ_AND_WRITE_ALLOCATE   4'b1110
`define AXI_AWCACHE_WRITE_BACK_NO_ALLOCATE                  4'b0111
`define AXI_AWCACHE_WRITE_BACK_READ_ALLOCATE                4'b0111
`define AXI_AWCACHE_WRITE_BACK_WRITE_ALLOCATE               4'b1111
`define AXI_AWCACHE_WRITE_BACK_READ_AND_WRITE_ALLOCATE      4'b1111

`define AXI_SIZE_BYTES_1                                    3'b000
`define AXI_SIZE_BYTES_2                                    3'b001
`define AXI_SIZE_BYTES_4                                    3'b010
`define AXI_SIZE_BYTES_8                                    3'b011
`define AXI_SIZE_BYTES_16                                   3'b100
`define AXI_SIZE_BYTES_32                                   3'b101
`define AXI_SIZE_BYTES_64                                   3'b110
`define AXI_SIZE_BYTES_128                                  3'b111

module axi4_crtl(
    input ACLK,
    input ARESETn,
    input flush,

    input inst_r_ena,
    input [63:0] inst_addr,
    output [63:0] inst_r,
    input data_r_ena,
    input data_w_ena,
    input [63:0] data_w,
    input [63:0] data_addr,
    input [7:0] data_w_mask,
    input wlast_i,
    output [63:0] data_r,

    output [3:0] ARID,
    output [63:0] ARADDR,
    output [7:0] ARLEN,
    output [2:0] ARSIZE,
    output [1:0] ARBURST,
    output [1:0] ARLOCK,
    output [3:0] ARCACHE,
    output [2:0] ARPROT,
    output [3:0] ARQOS,
    output [3:0] ARREGION,
    output ARVALID,
    input ARREADY,

    input [3:0] RID,
    input [63:0] RDATA,
    input RVALID,
    output RREADY,
    input RLAST,
    input [1:0] RRESP,

    output [3:0] AWID,
    output [63:0] AWADDR,
    output [7:0] AWLEN,
    output [3:0] AWSIZE,
    output [1:0] AWBURST,
    output [1:0] AWLOCK,
    output [3:0] AWCACHE,
    output [2:0] AWPROT,
    output [3:0] AWQOS,
    output [3:0] AWREGION,
    output AWVALID,
    input AWREADY,

    output [63:0] WDATA,
    output [7:0] WSTRB,
    output WVALID,
    input WREADY,
    output WLAST,

    input [3:0] BID,
    input BVALID,
    output BREADY,
    input [1:0] BRESP,

    output data_r_valid,
    output data_w_valid,
    output data_ar_valid
);

    parameter IDEL               = 3'b000;	//空闲
	parameter INST_SEND_RADDR    = 3'b001;	//指令cache发读地址
	parameter INST_RECEIVE_RDATA = 3'b010;	//指令cache接收读数据
	parameter DATA_SEND_RADDR    = 3'b011;	//数据cache发读地址
	parameter DATA_RECEIVE_RDATA = 3'b100;	//数据cache接收读数据
	parameter DATA_SEND_WADDR    = 3'b001;	//数据cache发写地址
	parameter DATA_SEND_WDATA    = 3'b010;	//数据cache发写数据
	parameter DATA_RECEIVE_B     = 3'b011;	//数据cache写响应

    wire ar_hs = ARREADY & ARVALID;
    wire r_hs = RREADY & RVALID;
    wire aw_hs = AWREADY & AWVALID;
    wire w_hs = WREADY & WVALID;
    wire b_hs = BREADY & BVALID;
    wire r_done = r_hs & RLAST;
    wire w_done = w_hs & WLAST;
	
	reg [2:0] axi_read_state;
    reg [2:0] axi_write_state;

    always @(posedge ACLK) begin
        if(ARESETn) begin
            axi_read_state <= 3'b000;
        end else begin
            case(axi_read_state)
                IDEL: begin
                    if(flush) begin
                        axi_read_state <= IDEL;
                    end else if(inst_r_ena) begin
                        axi_read_state <= INST_SEND_RADDR;
                    end else if(data_r_ena) begin
                        axi_read_state <= DATA_SEND_RADDR;
                    end
                end
                INST_SEND_RADDR : begin
                    if(ar_hs) begin
                        axi_read_state <= INST_RECEIVE_RDATA;
                    end
                end
                INST_RECEIVE_RDATA : begin
                    if(r_done) begin
                        axi_read_state <= IDEL;
                    end
                end
                DATA_SEND_RADDR : begin
                    if(ar_hs) begin
                        axi_read_state <= DATA_RECEIVE_RDATA;
                    end
                end
                DATA_RECEIVE_RDATA : begin
                    if(r_done) begin
                        axi_read_state <= IDEL;
                    end
                end
            endcase
        end
    end

    always @(posedge ACLK) begin
        if(ARESETn) begin
            axi_write_state <= 3'b000;
        end else begin
            case(axi_write_state) begin
                IDEL: begin
                    if(flush) begin
                        axi_write_state <= IDEL;
                    end else if(data_w_ena) begin
                        axi_write_state <= DATA_SEND_WADDR;
                    end
                end
                DATA_SEND_WADDR : begin
                    if(aw_hs) begin
                        axi_write_state <= DATA_SEND_WDATA;
                    end
                end
                DATA_SEND_WDATA : begin
                    if(w_done) begin
                        axi_write_state <= DATA_RECEIVE_B;
                    end
                end
                DATA_RECEIVE_B : begin
                    if(b_hs) begin
                        axi_write_state <= IDEL;
                    end
                end
            endcase    
        end
    end

    ///////////////////////////////////////////////////

    assign inst_r = (axi_read_state == INST_RECEIVE_RDATA) ? RDATA : 64'd0;
    assign data_r = (axi_read_state == DATA_RECEIVE_RDATA) ? RDATA : 64'd0;
    assign data_r_valid = (axi_read_state == )

    assign ARID = (axi_read_state == INST_SEND_RADDR) ? 4'b0001 : (axi_read_state == DATA_SEND_RADDR ? 4'b0010 : 4'b0000);
    assign ARADDR = (axi_read_state == INST_SEND_RADDR) ? inst_addr : (axi_read_state == DATA_SEND_RADDR ? data_addr : 64'd0);
    assign ARVALID = (axi_read_state == INST_SEND_RADDR || axi_read_state == DATA_SEND_RADDR) ? 1'b1 : 1'b0;
    assign ARLEN = 8'b00000001;
    assign ARSIZE = `AXI_SIZE_BYTES_64;
    assign ARBURST = `AXI_BURST_TYPE_INCR;
    assign ARLOCK = 1'b0;
    assign ARCACHE = `AXI_ARCACHE_NORMAL_NON_CACHEABLE_NON_BUFFERABLE;
    assign ARPORT = `AXI_PROT_UNPRIVILEGED_ACCESS;
    assign ARQOS = 4'b0000;
    assign ARREGION = 4'b0000;

    assign RREADY = (axi_read_state == INST_RECEIVE_RDATA || axi_read_state == DATA_RECEIVE_RDATA) ? 1'b1 : 1'b0;

    assign AWID = (axi_write_state == DATA_SEND_WADDR) ? 4'b0001 : 4'b0000;
    assign AWADDR = (axi_write_state == DATA_SEND_WADDR) ? data_addr : 64'd0;
    assign AWLEN = 8'b00000001;
    assign AWSIZE = `AXI_SIZE_BYTES_64;
    assign AWBURST = `AXI_BURST_TYPE_INCR;
    assign AWLOCK = 1'b0;
    assign AWCACHE = `AXI_AWCACHE_NORMAL_NON_CACHEABLE_NON_BUFFERABLE;
    assign AWPROT = `AXI_PROT_UNPRIVILEGED_ACCESS;
    assign AWQOS = 4'b0000;
    assign AWREGION = 4'b0000;
    assign AWVALID = ;

    assign WDATA = (axi_write_state == DATA_SEND_WDATA) ? data_w : 64'd0;
    assign WSTRB = (axi_write_state == DATA_SEND_WDATA) ? data_w_mask : 8'b00000000;
    assign WVALID = (axi_write_state == DATA_SEND_WDATA) ? 1'b1 : 1'b0;
    assign WLAST = wlast_i;

    assign BREADY = (axi_write_state == DATA_RECEIVE_B) ? 1'b1 : 1'b0;
endmodule